package com.fit.spring.boot.proxy.interceptor.autoconfigure;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.DefaultAopProxyFactory;
import org.springframework.aop.framework.ProxyFactoryBean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Import;

import com.fit.spring.boot.proxy.interceptor.extension.Config;
import com.fit.spring.boot.proxy.interceptor.extension.InterceptorChain;
import com.fit.spring.boot.proxy.interceptor.extension.InterceptorConfiguration;
import com.fit.spring.boot.proxy.interceptor.extension.MethodConfig;
import com.fit.spring.boot.proxy.interceptor.extension.ProxyExtensionMethodInterceptor;
import com.fit.spring.boot.proxy.interceptor.utils.AopTargetUtils;

/**
 * 代理扩展自动启动配置类
 * @author hcl
 *
 */
@Import(ProxyExtensionProperties.class)
public class ProxyExtensionAutoConfiguration implements BeanPostProcessor{

	private static Logger log = LoggerFactory.getLogger(ProxyExtensionAutoConfiguration.class);
	
	/**
	 * 配置对象
	 */
	private ProxyExtensionProperties proxyExtensionProperties;
	
	/**
	 * 代理拦截器配置器
	 */
	private List<InterceptorConfiguration> interceptorConfigurations = new ArrayList<InterceptorConfiguration>();
	
	/**
	 * 构造方法初始化
	 * @param proxyExtensionProperties
	 * @param interceptorConfigurationMap
	 */
	public ProxyExtensionAutoConfiguration(
			ProxyExtensionProperties proxyExtensionProperties,
			ApplicationContext context){
		this.proxyExtensionProperties = proxyExtensionProperties;
		if(proxyExtensionProperties.getEnabled()){
			this.interceptorConfigurations.addAll(context.getBeansOfType(InterceptorConfiguration.class).values());
		}else{
			log.warn("Shut down the Proxy Extension! config attribute enabled is '{}'。", proxyExtensionProperties.getEnabled());
		}
	}
	
	/**
	 * bean初始化前置处理器
	 */
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

	/**
	 * bean初始化后置处理器
	 */
	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		if(proxyExtensionProperties.getEnabled()){
			Class<?> targetClass = getTargetClass(bean);
			//获取拦截器链
			Map<Method, InterceptorChain> methodInterceptorChain = getMethodInterceptorChain(targetClass);
			if(methodInterceptorChain.size() > 0){
				return createProxy(bean, methodInterceptorChain);
			}
		}
		return bean;
	}
	
	/**
	 * 获取方法拦截器链
	 * @param targetClass
	 * @return
	 */
	private Map<Method, InterceptorChain> getMethodInterceptorChain(Class<?> clazz){
		
		if(clazz.getName().equals("net.linkcn.module.test.web.controller.TestUserController")){
			System.out.println("匹配");
		}
		
		Map<Method, InterceptorChain> methodInterceptorChain = new HashMap<Method, InterceptorChain>();
		
		//获取bean Method拦截器
		Method[] methods = clazz.getMethods();
		for(Method method : methods){
			List<MethodConfig> methodInterceptors = new ArrayList<MethodConfig>();;
			//获取拦截器
			for(InterceptorConfiguration ic : interceptorConfigurations){
				MethodInterceptor methodInterceptor = ic.getMethodInterceptor();
				Config config = new Config(clazz,method);
				if(ic.isMethodInterceptor(config)){
					methodInterceptors.add(new MethodConfig(methodInterceptor,config.getMethodParam()));
				}
			}
			InterceptorChain interceptorChain = createInterceptorChain(methodInterceptors);
			if(interceptorChain != null){
				methodInterceptorChain.put(method, interceptorChain);
			}
		}
		return methodInterceptorChain;
	}
	
	
	
	/**
	 * 创建拦截器链
	 * @param interceptorsList
	 * @return
	 */
	private InterceptorChain createInterceptorChain(List<MethodConfig> interceptors){
		if(interceptors != null){
			interceptors.removeAll(Collections.singleton(null));
			if(interceptors.size() > 0){
				InterceptorChain firstInterceptorChain = new InterceptorChain();
				firstInterceptorChain.setMethodConfig(interceptors.get(0));
				if(interceptors.size() > 1){
					InterceptorChain interceptorChain = firstInterceptorChain;
					for(int i=1,len=interceptors.size(); i<len; i++){
						InterceptorChain chain = new InterceptorChain();
						chain.setMethodConfig(interceptors.get(i));
						interceptorChain.setNextInterceptorChain(chain);
						interceptorChain = chain;
					}
				}
				return firstInterceptorChain;
			}
		}
		return null;
	}
	
	/**
	 * 创建代理对象,并初始化代理对象拦截器
	 * @param bean
	 * @param methodInterceptorChain
	 * @return
	 */
	private Object createProxy(Object bean,Map<Method, InterceptorChain> methodInterceptorChain){
		if(methodInterceptorChain == null || methodInterceptorChain.size() == 0){
			return bean;
		}
		ProxyFactoryBean config = new ProxyFactoryBean();
		config.setTarget(bean);
		//创建通知
		Advice advice = new ProxyExtensionMethodInterceptor(methodInterceptorChain);
		config.addAdvice(advice);
		DefaultAopProxyFactory aopFactory = new DefaultAopProxyFactory();
		return aopFactory.createAopProxy(config).getProxy();
	}
	
	/**
	 * 获取目标class
	 * @param bean
	 * @return
	 */
	private static Class<?> getTargetClass(Object bean){
		try {
			return AopTargetUtils.getTarget(bean).getClass();
		} catch (Exception e) {
			throw new RuntimeException("获代理bean目标class异常！class = '" + bean.getClass().getName() + "'",e);
		}
	}
	
}
